package com.apes.fn.scm.merchant.account.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fn.fico.debitnote.model.BusinessDebitNote;
import com.apes.fn.fico.debitnote.model.DeductionDetail;
import com.apes.fn.fico.debitnote.repository.BusinessDebitNoteRepository;
import com.apes.fn.fico.debitnote.repository.DeductionDetailRepository;
import com.apes.fn.fico.expense.ExpenseItem;
import com.apes.fn.fico.expense.ExpenseItemRepository;
import com.apes.fn.rpt.settle.repository.SummaryDao;
import com.apes.fn.scm.merchant.account.model.*;
import com.apes.fn.scm.merchant.account.repository.AccountSettlementRepository;
import com.apes.fn.scm.merchant.account.repository.MerchantBankAccountRepository;
import com.apes.fn.scm.merchant.account.repository.MerchantFixedLossRepository;
import com.apes.fn.scm.merchant.account.repository.SettlementExpenseItemRepository;
import com.apes.fn.scm.merchant.settlement.model.CostSummary;
import com.apes.fn.scm.merchant.settlement.model.MerchantSettlement;
import com.apes.fn.scm.merchant.settlement.service.MerchantSettlementService;
import com.apes.fn.scm.prepare.model.Prepare;
import com.apes.fn.scm.prepare.model.ToPrepare;
import com.apes.fn.scm.prepare.repository.PrepareRepository;
import com.apes.fn.scm.prepare.repository.ToPrepareRepository;
import com.apes.fn.scm.settleCheck.model.PartnerCheck;
import com.apes.fn.scm.settleCheck.reponsitory.PartnerCheckRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.generator.service.SequenceService;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.Arith;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.MapUtil;
import com.apes.scm.account.account.repository.InventoryBatchRepository;
import com.apes.scm.account.ledger.service.LedgerService;
import com.apes.scm.income.model.Income;
import com.apes.scm.income.model.IncomeItem;
import com.apes.scm.income.repository.IncomeItemRepository;
import com.apes.scm.income.repository.IncomeRepository;
import com.apes.scm.masterdata.enumeration.model.Enumeration;
import com.apes.scm.masterdata.enumeration.repository.EnumerationRepository;
import com.apes.scm.masterdata.finance.model.BankAccount;
import com.apes.scm.masterdata.finance.repository.BankAccountRepository;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.model.Store;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import com.apes.scm.masterdata.frame.repository.StoreRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.rbac.model.Post;
import com.apes.scm.rbac.model.User;
import com.apes.scm.rbac.repository.PostRepository;
import com.apes.scm.rbac.repository.UserRepository;
import com.apes.scm.settle.payment.model.Payment;
import com.apes.scm.settle.payment.model.PaymentItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toMap;

/**
 * @BelongsProject: apes-server
 * @BelongsPackage: com.apes.fn.scm.merchant.account.service
 * @ClassName: AccountSettlementService
 * @Author: pyb
 * @Description: 赋能商户结算对账业务层
 * @CreateTime: 2021-10-01 15:11
 * @Version: 1.0
 */
@Service("accountSettlementService")
public class AccountSettlementService extends DomainService {

    @Autowired
    private PartnerCheckRepository partnerCheckRepository;

    @Autowired
    private AccountSettlementRepository settlementRepository;

    @Autowired
    private AccountSettlementDao accountSettlementDao;

    @Autowired
    private PartyRoleRepository partyRoleRepository;

    @Autowired
    private BusinessDebitNoteRepository businessDebitNoteRepository;

    @Autowired
    private IncomeRepository incomeRepository;

    @Autowired
    private SummaryDao summaryDao;

    @Autowired
    private MerchantFixedLossRepository merchantFixedLossRepository;

    @Autowired
    private IncomeItemRepository incomeItemRepository;

    @Autowired
    private LedgerService ledgerService;

    @Autowired
    private MerchantBankAccountRepository merchantBankAccountRepository;

    @Autowired
    private SettlementExpenseItemRepository settlementExpenseItemRepository;

    @Autowired
    private ExpenseItemRepository expenseItemRepository;

    @Autowired
    private DeductionDetailRepository deductionDetailRepository;

    @Autowired
    private ToPrepareRepository toPrepareRepository;

    @Autowired
    private PrepareRepository prepareRepository;


    /**
     * 生成赋能对账单数据
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement generateSettlementData(SimpleRequest request) {
        //处理请求参数
        JSONObject requestJO = request.getJO();
        String merchantId = requestJO.getString("merchantId");
        String companyId = requestJO.getString("companyId");
        Date reconcileBeginDate = DateUtil.dateToDate(requestJO.getDate("reconcileBeginDate"), DateUtil.SHOW_DATE_FORMAT);
        Date reconcileEndDate = DateUtil.dateToDate(requestJO.getDate("reconcileEndDate"), DateUtil.SHOW_DATE_FORMAT);
        //构建对账单
        Store store = SpringManager.getBean(StoreRepository.class).findOne(SpringManager.getBean(DeptRepository.class)
                .findByPartyRoleAndDeptType(merchantId, "000007").getId());
        if (Objects.isNull(store.getFnTag()))
            throw new RuntimeException("[" + store.getDept().getId() + "]" + store.getDept().getName() + "，没有维护赋能标签，请联系行政在门店信息维护！");
        MerchantAccountSettlement settlement = new MerchantAccountSettlement();
        //检查与商户签订的政策
        checkPolicy(merchantId, reconcileBeginDate, reconcileEndDate);
        //获取上期对账数据
        MerchantAccountSettlement previousMerchantAccountSettlement = getThePreviousMerchantSettlement(reconcileBeginDate, merchantId);

        //写入对账期间
        settlement.setFnTag(store.getFnTag());
        settlement.setReconcileEndDate(reconcileEndDate);
        settlement.setReconcileBeginDate(reconcileBeginDate);
        //常备库存金额
        settlement.setInventoryMoney(SpringManager.getBean(InventoryBatchRepository.class).findStandingInventoryAmount(merchantId));
        settlement.setPreSettleBusinessMoney(Objects.nonNull(previousMerchantAccountSettlement) && previousMerchantAccountSettlement.getSettleBusinessMoneyTotal() < 0 ? previousMerchantAccountSettlement.getSettleBusinessMoneyTotal() : 0);


        //获取上期对账数据(旧版对账单)
        MerchantSettlement previousMerchantSettlement = previousMerchantAccountSettlement != null ? null :
                SpringManager.getBean(MerchantSettlementService.class).getThePreviousMerchantSettlement(merchantId, reconcileBeginDate);
        //获取上期成本
        Map<String, CostSummary> costSummaryMap = previousMerchantSettlement == null ? new HashMap<>() : previousMerchantSettlement
                .getCostSummaries().stream().collect(Collectors.toMap(costSummary -> costSummary.getCostSummaryType().getId(),
                        costSummary -> costSummary, (k1, k2) -> k1));
        //写入收入数据(营业收入汇总)
        writeIncomeMethod(merchantId, reconcileBeginDate, reconcileEndDate, settlement, previousMerchantAccountSettlement);
        //写入成本数据(营业支出汇总)
        writeCostMethod(merchantId, reconcileBeginDate, reconcileEndDate, previousMerchantAccountSettlement, settlement, costSummaryMap);
        //获取待扣收费用单
        getExpenseItems(merchantId, settlement);
        //获取待扣借支单
        getBusinessDebitItem(merchantId, settlement);
        //计算汇总金额
        settlement.setAccumulate();
        settlement.getAccumulateSum();
        settlement.computeAmt();
        settlement.setAccumulateSum(Arith.round(settlement.getSettleBusinessMoneyTotal() < 1 ? settlement.getSettleBusinessMoneyTotal() : 0 - settlement.getTotalUnDeductedCost(), 2));//累计欠款金额
        settlement.setSettleBusinessMoneyTotal();//本期应结算营业款
        //查询收款账户
        setMerchantBankAccounts(merchantId, settlement);
        //科目余额
        getMerchantAccountSubjects(merchantId, companyId, settlement, requestJO.getString("reconcileEndDate"), previousMerchantAccountSettlement);

        //根据枚举类型进行排序
        settlement.setIncomeSummaries(settlement.getIncomeSummaries().stream().sorted(Comparator.comparing(SettlementIncomeSummary::getEnumeration)).collect(Collectors.toList()));
        settlement.setCostSummaries(settlement.getCostSummaries().stream().sorted(Comparator.comparing(SettlementCostSummary::getEnumeration)).collect(Collectors.toList()));
        return settlement;
    }

    /**
     * 查询科目余额
     *
     * @param settlement
     */
    private void getMerchantAccountSubjects(String merchantId, String companyId, MerchantAccountSettlement settlement, String date, MerchantAccountSettlement lastPeriods) {
        MerchantAccountSubjects subject = new MerchantAccountSubjects();
        List<MerchantAccountSubjects> merchantBankSubjectsList = new ArrayList<>();
        MerchantAccountSubjects subjects = new MerchantAccountSubjects();
        //前期
        if (Objects.nonNull(lastPeriods)) {
            if (lastPeriods.getMerchantAccountSubjects().size() > 0) {
                subject = getMerchantAccountSubjectsLastPeriods(lastPeriods);
                merchantBankSubjectsList.add(subject);
            }
        }

        String substring = date.substring(0, 8) + "01";
        List<Map> subjectBalance = summaryDao.findSubjectBalance(merchantId, companyId, substring, settlement.getReconcileEndDate());

        //本期
        subjectBalance.forEach(balance -> {
            if ("22410501".equals(balance.get("CODE"))) {
                subjects.setPlatformSupplier(Arith.round(Double.parseDouble(balance.get("BALANCE").toString()), 2));
            }
            if ("22410500".equals(balance.get("CODE"))) {
                subjects.setOutsideGroup(Arith.round(Double.parseDouble(balance.get("BALANCE").toString()), 2));
            }
            if ("22411004".equals(balance.get("CODE"))) {
                subjects.setEmpower(Arith.round(Double.parseDouble(balance.get("BALANCE").toString()), 2));
            }
            if ("12210109".equals(balance.get("CODE"))) {
                subjects.setOutsideGroupEmpower(Arith.round(Double.parseDouble(balance.get("BALANCE").toString())*-1, 2));
            }
        });
        merchantBankSubjectsList.add(subjects);
        settlement.setMerchantAccountSubjects(merchantBankSubjectsList.stream().sorted(Comparator.comparing(MerchantAccountSubjects::getPast).reversed()).collect(Collectors.toList()));
    }

    /**
     * 上期科目余额
     *
     * @param lastPeriods
     * @return
     */
    private MerchantAccountSubjects getMerchantAccountSubjectsLastPeriods(MerchantAccountSettlement lastPeriods) {
        //获取上期科目余额
        MerchantAccountSubjects subject = new MerchantAccountSubjects();
        lastPeriods.getMerchantAccountSubjects().stream().filter(p -> p.getPast() != true).forEach(p -> {
            subject.setPlatformSupplier(p.getPlatformSupplier());
            subject.setOutsideGroup(p.getOutsideGroup());
            subject.setEmpower(p.getEmpower());
            subject.setOutsideGroupEmpower(p.getOutsideGroupEmpower());
            subject.setInvoicePayable(p.getInvoicePayable());
            subject.setAccrualService(p.getAccrualService());
            subject.setReward(p.getReward());
            subject.setAmount(p.getAmount());
            subject.setActualPayable(p.getActualPayable());
            subject.setDifference(p.getDifference());
            subject.setNote(p.getNote());
            subject.setPast(true);
        });

        return subject;
    }

    /**
     * 查询银行账户
     *
     * @param merchantId
     * @param settlement
     */
    private void setMerchantBankAccounts(String merchantId, MerchantAccountSettlement settlement) {
        List<BankAccount> bankAccounts = SpringManager.getBean(BankAccountRepository.class).findAll(JpaDsl.toCriteriaByEq("company.id", merchantId, "valid", true));
        List<MerchantBankAccount> merchantBankAccounts = new ArrayList<>();
        Double sum = settlement.getSettleBusinessMoneyTotal();
        bankAccounts.forEach(bankAccount -> {
            MerchantBankAccount merchantBankAccount = new MerchantBankAccount();
            if (bankAccounts.size() == 1) {
                double amount = settlement.getSettleBusinessMoneyTotal() < 0 ? 0 : settlement.getSettleBusinessMoneyTotal();
                merchantBankAccount.setAmount(Arith.round(amount, 2));
            }
            merchantBankAccount.setName(bankAccount.getAccountName());
            merchantBankAccount.setBank(bankAccount.getBank());
            merchantBankAccount.setBankAccountId(bankAccount.getAccountId());
            merchantBankAccount.setAccountId(bankAccount);
            merchantBankAccount.setStandbyType(bankAccount.getType());
            merchantBankAccounts.add(merchantBankAccount);
        });
        settlement.setSettleBusinessMoneyTotal(sum);
        settlement.setMerchantBankAccounts(merchantBankAccounts);
    }

    /**
     * 写入营业收入数据(营业收入汇总)
     *
     * @param merchantId
     * @param dateEnd
     * @param dateEnd
     * @param settlement
     */
    public List<SettlementIncomeSummary> writeIncomeMethod(String merchantId, Date dateEnd, Date dateStart, MerchantAccountSettlement settlement, MerchantAccountSettlement previousMerchantSettlement) {
        //获取收入项目枚举
        List<String> stringList = Arrays.asList("receivable", "cash", "crossIncome", "receivableOut", "lossNotRefund");
        List<Enumeration> incomeSummaryEnum = SpringManager.getBean(EnumerationRepository.class).findEnumerationId(stringList);

        Map<String, Map> incomeSummaryMap = summaryDao.findIncomeSummary(merchantId, dateEnd, dateStart).stream().collect(toMap(map ->
                map.get("INCOME_TYPE").toString(), map -> map, (k1, k2) -> k1));
        List<SettlementIncomeSummary> incomeSummaries = new ArrayList<>();
        //从待结算营业收入汇总报表写入对账单
        incomeSummaryEnum.forEach(income -> {
            Map incomeMap = incomeSummaryMap.get(income.getId());
            SettlementIncomeSummary incomeSummary = new SettlementIncomeSummary();
            incomeSummary.setIncomeSummary(income);
            incomeSummary.setFormId(SettlementFormEnum.getSettlementFormEnumByOrderType(income.getId()).getFromId());
            if ("lossNotRefund".equals(income.getId())) {
                incomeSummary.setAmount(fixedLoss(merchantId, dateEnd, dateStart, settlement));//获取定损未回款
            } else {
                incomeSummary.setAmount(incomeMap == null || incomeMap.get("PAYMENT_AMOUNT") == null ? 0 : Double.parseDouble(incomeMap.get("PAYMENT_AMOUNT").toString()));
            }
            if ("receivableOut".equals(income.getId())) {
                Calendar tempEnd = Calendar.getInstance();
                tempEnd.setTime(dateStart);
                tempEnd.add(Calendar.DATE, +1);// 日期加1(包含结束)

                //查找当前止日期前所有待缴款的应收账款外
                double toPrepareAmount = toPrepareRepository.findAll(JpaDsl.toCriteriaIgnoreNull("createDate", "LTE", tempEnd.getTime(), "company.id", "EQ",
                        merchantId, "paymentType.id", "EQ", "002036")).stream().mapToDouble(ToPrepare::getAmount).sum();

                double prepareAmount = prepareRepository.findAll(JpaDsl.toCriteriaIgnoreNull("state", "NE", "repeal", "approveDate", "LTE", tempEnd.getTime(), "company.id", "EQ",
                        merchantId, "paymentType.id", "EQ", "002036")).stream().mapToDouble(Prepare::getPrepareAmount).sum();
                incomeSummary.setPlatformServiceFee(Arith.round(toPrepareAmount - prepareAmount, 2));//本期平台未入账

                if (Objects.nonNull(previousMerchantSettlement)) {
                    double platformServiceFee = previousMerchantSettlement.getIncomeSummaries().stream().filter(v -> "receivableOut".equals(v.getIncomeSummary().getId())).mapToDouble(SettlementIncomeSummary::getPlatformServiceFee).sum();
                    incomeSummary.setPreviousServiceFee(Arith.round(platformServiceFee, 2));
                }


            } else {
                incomeSummary.setPlatformServiceFee(incomeMap == null || incomeMap.get("PLATFORM_MANAGEMENT_FEE") == null ? 0 :
                        Double.parseDouble(incomeMap.get("PLATFORM_MANAGEMENT_FEE").toString()));
            }
            incomeSummary.setAmountPayable(Arith.round(incomeSummary.getAmount() + incomeSummary.getPreviousServiceFee() - incomeSummary.getPlatformServiceFee() - incomeSummary.getAdjustmentAmount(), 2));
            incomeSummaries.add(incomeSummary);
        });
        settlement.setIncomeSummaries(incomeSummaries.stream().sorted(Comparator.comparing(SettlementIncomeSummary::getEnumeration)).collect(Collectors.toList()));
        return incomeSummaries;
    }

    public Double fixedLoss(String merchantId, Date dateEnd, Date dateStart, MerchantAccountSettlement settlement) {
        merchantFixedLossRepository.deleteAll(merchantFixedLossRepository.findCompany(merchantId,
                DateUtil.format(dateEnd, DateUtil.SHOW_DATE_FORMAT), DateUtil.format(dateStart, DateUtil.SHOW_DATE_FORMAT)));
        List<MerchantFixedLoss> merchantFixedLosses = new ArrayList<>();
        List<Map> mapList = new ArrayList<>();
        Dept dept = SpringManager.getBean(DeptRepository.class).findAll(JpaDsl.toCriteriaByEq("company.id",
                merchantId, "deptType.id", "000007", "valid", true)).stream().findFirst().orElse(null);
        if (dept == null) return 0.0;
        //获取本期定损金额的零售单
        summaryDao.findUrrentIssue(dept.getId(), dateEnd, dateStart).stream().forEach(map -> {
            mapList.add(map);
        });
        mapList.forEach(map -> {
            MerchantFixedLoss merchantFixedLos = new MerchantFixedLoss();
            try {
                merchantFixedLos.setBookkeepingDate(new SimpleDateFormat("yyyy-MM-dd").parse(map.get("POST_DATE").toString()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            merchantFixedLos.setCarLicense(map.get("CAR_LICENSE") == null ? null : map.get("CAR_LICENSE").toString());
            merchantFixedLos.setSource(map.get("ID").toString());
            merchantFixedLos.setSourceBusinessType(map.get("INNER_BUSINESS_TYPE").toString());
            merchantFixedLos.setCollectionAmount(map.get("AMOUNT") == null ? 0 : Double.valueOf(map.get("AMOUNT").toString()));
            merchantFixedLos.setDept(dept);
            merchantFixedLos.setCompany(partyRoleRepository.findById(merchantId).get());
            merchantFixedLos.setRemark(map.get("REMARK") == null ? null : map.get("REMARK").toString());
            merchantFixedLos.setCreateDate(new Date());
            merchantFixedLosses.add(merchantFixedLos);
        });

        //获取集群账户,监管账户
//        double sum = summaryDao.findAccount(dept.getId(), dateEnd, dateStart).stream().mapToDouble(num -> Double.parseDouble(String.valueOf(num.get("AMOUNT")))).sum();

        merchantFixedLossRepository.saveAll(merchantFixedLosses);

        settlement.setLastPeriodAmount(merchantFixedLosses.stream().mapToDouble(MerchantFixedLoss::getCollectionAmount).sum());
        return Arith.round(settlement.getLastPeriodAmount() * -1, 2);
    }

    /**
     * 写入成本汇总
     *
     * @param merchantId
     * @param reconcileBeginDate
     * @param reconcileEndDate
     * @param previousMerchantSettlement
     * @param settlement
     */
    public List<SettlementCostSummary> writeCostMethod(String merchantId, Date reconcileBeginDate, Date reconcileEndDate, MerchantAccountSettlement previousMerchantSettlement,
                                                       MerchantAccountSettlement settlement, Map<String, CostSummary> costSummaryMap) {
        //获取成本项目枚举
        List<String> stringList = Arrays.asList("OWN_MONEY", "CONSIGN_MONEY", "PLATFORM_FEE", "crossPay", "BORROW_MONEY");
        List<Enumeration> expenditureSummary = SpringManager.getBean(EnumerationRepository.class).findEnumerationId(stringList);
        //获取上期成本
        Map<String, SettlementCostSummary> periodCostSummaryMap = previousMerchantSettlement == null ? new HashMap<>() :
                previousMerchantSettlement.getCostSummaries().stream().collect(Collectors.toMap(costSummary ->
                        costSummary.getExpenditureSummary().getId(), costSummary -> costSummary, (k1, k2) -> k1));
        //查询待结算营业支出汇总报表数据(本期)
        Map<String, Double> costMap = summaryDao.findCostSummary(merchantId, reconcileBeginDate, reconcileEndDate)
                .stream().collect(toMap(map -> map.get("EXPENDITURE_TYPE").toString(), map -> Double.valueOf(map.get("AMOUNT").toString()), (k1, k2) -> k1));
        List<SettlementCostSummary> costSummaries = new ArrayList<>();
        //从待结算营业支出汇总报表写入对账单
        expenditureSummary.forEach(cost -> {
            //获取上期未扣收
            double balanceAmount = periodCostSummaryMap.size() < 1 ? 0 : periodCostSummaryMap.get(cost.getId().equals("crossPay") ? "PLATFORM_FEE" : cost.getId()).getRemainingDeductibleBalance();
            SettlementCostSummary costSummary = new SettlementCostSummary();
            if ("OWN_MONEY".equals(cost.getId())) {//经销商品货款
                costSummary.setPreviousNotDeducted(Arith.add(balanceAmount, costSummaryMap.isEmpty() ? 0 : costSummaryMap.get("operatingCosts").getRemainingDeductibleBalance()));
            } else {
                costSummary.setPreviousNotDeducted(periodCostSummaryMap.size() == 0 ? 0 : periodCostSummaryMap.get(cost.getId()).getRemainingDeductibleBalance());
            }

            costSummary.setExpenditureSummary(cost);
            costSummary.setFormId(SettlementFormEnum.getSettlementFormEnumByOrderType(cost.getId()).getFromId());
            costSummary.setCurrentPeriodShouldDeducted(Arith.round(costMap.get(cost.getId()) == null ? 0 : costMap.get(cost.getId()), 2));//本期新增金额
            costSummary.setCurrentPeriodActualDeducted(Arith.round(costSummary.getPreviousNotDeducted()+costSummary.getCurrentPeriodShouldDeducted(), 2));//本期实际扣收
            //剩余应扣余额
            costSummary.setRemainingDeductibleBalance(Arith.round(costSummary.getPreviousNotDeducted()+costSummary.getCurrentPeriodShouldDeducted() - costSummary.getCurrentPeriodActualDeducted(), 2));
            costSummaries.add(costSummary);
        });

        settlement.setCostSummaries(costSummaries.stream().sorted(Comparator.comparing(SettlementCostSummary::getEnumeration)).collect(Collectors.toList()));
        return costSummaries;
    }

    /**
     * 保存方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement save(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        List<MerchantAccountSettlement> accountSettlements = settlementRepository.findAll(JpaDsl.toCriteriaByEq("reconcileBeginDate", settlement.getReconcileBeginDate(), "reconcileEndDate",
                settlement.getReconcileEndDate(), "merchant.id", settlement.getMerchant().getId()));
        if (accountSettlements.size() > 0)
            throw new RuntimeException(settlement.getMerchant().getId() + "本期已被创建，无法再次创建！");
        if (settlement.getMerchantBankAccounts().size() < 1) throw new RuntimeException("收款账户不能为空！请添加收款账户！！！");

        settlement.computeAmt();
        settlement.setAccumulate();
        settlement.getAccumulateSum();
        settlement.setState("sent");
        settlement.setReconcileUid(request.getPerson());
        settlement.setReconcileDate(new Date());
        settlement.setAccumulate();
        settlement.getAccumulateSum();

        //重新获取分期剩余未扣收金额
        double remainingDeductibleBalance = settlement.getCostSummaries().stream().mapToDouble(SettlementCostSummary::getRemainingDeductibleBalance).sum();
        settlement.setTotalUnDeductedCost(Arith.round(remainingDeductibleBalance, 2));

        // 总计应结算金额
        settlement.setSettleBusinessMoneyTotal(Arith.round(settlement.getSettleBusinessMoneyTotal(), 2));

        //重新给收款账户赋值，并且判断是否和总计应结算营业额一致
        if (settlement.getMerchantBankAccounts().size() == 1) {
            setMerchantBankAccount(settlement);
        }
        // 重新获取平台费用，写入营业支出平台费用
        costSummsryUpdate(settlement);
        //回写费用账单
        freezeIncomeItem(settlement, 1);
        if (settlement.getSettleBusinessMoneyTotal() <= 0 || settlement.getMerchantBankAccounts().size() == 1) {
            settlement.setTotalOa("0");
        } else {
            settlement.setTotalOa("1");
        }
        settlementRepository.saveAndFlush(settlement);

        businessDebitNoteCreate(settlement);//回写预支单
        return settlement;
    }

    /*
     * 修改费用账单冻结金额（sign: 1、-1）
     *
     */
    private void freezeIncomeItem(MerchantAccountSettlement settlement, int sign) {
        //回写费用账单
        settlement.getExpenseItems().forEach(item -> {
            IncomeItem incomeItem = item.getIncomeItem();
            PartyRole partyRole = incomeItem.getIncome().getPartner();
            double sum = "Supplier".equals(partyRole.getRole().getId()) ? item.getCurrentAmount() * -1 : item.getCurrentAmount();
            incomeItem.setFreezeAmount(incomeItem.getFreezeAmount() + sign * sum);
            incomeItem.updateDone();
            if (incomeItem.getIncomeAmount() == 0 && sign == -1) {
                incomeItem.setDone(false);
            }
            incomeItemRepository.saveAndFlush(incomeItem);
        });
    }

    /**
     * 保存回写预支单
     *
     * @param settlement
     */
    private void businessDebitNoteCreate(MerchantAccountSettlement settlement) {
        //回写费用账单
        settlement.getBusinessDebitItems().forEach(item -> {
            BusinessDebitNote businessDebitNote = businessDebitNoteRepository.findOne(item.getBusinessDebitNote().getId());

            DeductionDetail deductionDetail = new DeductionDetail();
            deductionDetail.setSettlement(settlement);
            deductionDetail.setRepaymentAmount(deductionDetail.getRepaymentAmount() + item.getCurrentAmount());//已扣收金额
            deductionDetail.setCurrentPrincipal(deductionDetail.getCurrentPrincipal() + item.getBalanceAmount());//未扣收金额
            deductionDetail.setBusinessDebitNote(businessDebitNote);//预支单

            businessDebitNote.setUnDeductedLoanAmount(businessDebitNote.getUnDeductedLoanAmount() - item.getCurrentAmount());//未扣收预支金额
            businessDebitNote.setUnBuckledBudgetAmount(businessDebitNote.getUnBuckledBudgetAmount() + item.getCurrentAmount());//已扣收预支金额
            if (businessDebitNote.getCapitalBalance() == businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("paidOff");//已扣收
            } else if (businessDebitNote.getUnBuckledBudgetAmount() > 0 && businessDebitNote.getCapitalBalance() != businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("partialDeduction");//部分扣收
            } else {
                businessDebitNote.setWithhold("unwithheld");//未扣收
            }
            businessDebitNote.getDeductionDetails().add(deductionDetailRepository.saveAndFlush(deductionDetail));
            businessDebitNoteRepository.saveAndFlush(businessDebitNote);
            item.setDeductionDetail(deductionDetail);
        });
    }

    /**
     * 还原预支单
     *
     * @param settlement
     */
    private void businessDebitNoteUpdate(MerchantAccountSettlement settlement) {
        //回写费用账单
        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.findOne(settlement.getId());
        merchantAccountSettlement.getBusinessDebitItems().forEach(item -> {
            DeductionDetail deductionDetail = deductionDetailRepository.findOne(item.getDeductionDetail().getId());
            deductionDetail.setRepaymentAmount(deductionDetail.getRepaymentAmount() - item.getCurrentAmount());
            deductionDetail.setCurrentPrincipal(deductionDetail.getCurrentPrincipal() - item.getBalanceAmount());

            BusinessDebitNote businessDebitNote = businessDebitNoteRepository.findOne(deductionDetail.getBusinessDebitNote().getId());
            businessDebitNote.setUnDeductedLoanAmount(businessDebitNote.getUnDeductedLoanAmount() + item.getCurrentAmount());//未扣收预支金额
            businessDebitNote.setUnBuckledBudgetAmount(businessDebitNote.getUnBuckledBudgetAmount() - item.getCurrentAmount());//已扣收预支金额
            if (businessDebitNote.getCapitalBalance() == businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("paidOff");//已扣收
            } else if (businessDebitNote.getUnBuckledBudgetAmount() > 0 && businessDebitNote.getCapitalBalance() != businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("partialDeduction");//部分扣收
            } else {
                businessDebitNote.setWithhold("unwithheld");//未扣收
            }
            businessDebitNote.getDeductionDetails().add(deductionDetail);
            businessDebitNoteRepository.saveAndFlush(businessDebitNote);
        });
    }

    /**
     * 删除预支单
     *
     * @param settlement
     */
    private void businessDebitNoteDelete(MerchantAccountSettlement settlement) {
        //回写费用账单
        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.findOne(settlement.getId());
        merchantAccountSettlement.getBusinessDebitItems().forEach(item -> {
            BusinessDebitNote businessDebitNote = businessDebitNoteRepository.findOne(item.getBusinessDebitNote().getId());
            businessDebitNote.setUnBuckledBudgetAmount(businessDebitNote.getUnBuckledBudgetAmount() - item.getCurrentAmount());//已扣收预支金额
            businessDebitNote.setUnDeductedLoanAmount(businessDebitNote.getUnDeductedLoanAmount() + item.getCurrentAmount());//未扣收预支金额

            if (businessDebitNote.getCapitalBalance() == businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("paidOff");//已扣收
            } else if (businessDebitNote.getUnBuckledBudgetAmount() > 0 && businessDebitNote.getCapitalBalance() != businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("partialDeduction");//部分扣收
            } else {
                businessDebitNote.setWithhold("unwithheld");//未扣收
            }
            deductionDetailRepository.delete(item.getDeductionDetail());
        });
    }


    /**
     * 重写预支单
     *
     * @param settlement
     */
    private void businessDebitNoteSave(MerchantAccountSettlement settlement) {
        //回写费用账单
        settlement.getBusinessDebitItems().forEach(item -> {
            DeductionDetail deductionDetail = deductionDetailRepository.findOne(item.getDeductionDetail().getId());
            deductionDetail.setRepaymentAmount(deductionDetail.getRepaymentAmount() + item.getCurrentAmount());
            deductionDetail.setCurrentPrincipal(deductionDetail.getCurrentPrincipal() + item.getBalanceAmount());

            BusinessDebitNote businessDebitNote = businessDebitNoteRepository.findOne(deductionDetail.getBusinessDebitNote().getId());
            businessDebitNote.setUnDeductedLoanAmount(businessDebitNote.getUnDeductedLoanAmount() - item.getCurrentAmount());//未扣收预支金额
            businessDebitNote.setUnBuckledBudgetAmount(businessDebitNote.getUnBuckledBudgetAmount() + item.getCurrentAmount());//已扣收预支金额

            if (businessDebitNote.getCapitalBalance() == businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("paidOff");//已扣收
            } else if (businessDebitNote.getUnBuckledBudgetAmount() > 0 && businessDebitNote.getCapitalBalance() != businessDebitNote.getUnBuckledBudgetAmount()) {
                businessDebitNote.setWithhold("partialDeduction");//部分扣收
            } else {
                businessDebitNote.setWithhold("unwithheld");//未扣收
            }

            businessDebitNote.getDeductionDetails().add(deductionDetail);
            businessDebitNoteRepository.saveAndFlush(businessDebitNote);
        });
    }

    /**
     * 清账回写费用账单
     *
     * @param settlement
     */
    private void backIncomeItem(MerchantAccountSettlement settlement) {
        //回写费用账单
        settlement.getExpenseItems().forEach(item -> {
            IncomeItem incomeItem = incomeItemRepository.findOne(item.getIncomeItemId());
            PartyRole partyRole = partyRoleRepository.findOne(incomeItem.getIncome().getPartner().getId());
            double sum = "Supplier".equals(partyRole.getRole().getId()) ? item.getCurrentAmount() * -1 : item.getCurrentAmount();
            incomeItem.setFreezeAmount(incomeItem.getFreezeAmount() - sum);
            incomeItem.setRecoveryAmount(incomeItem.getRecoveryAmount() + sum);
            incomeItem.updateDone();
            incomeItemRepository.saveAndFlush(incomeItem);
        });
    }

    /**
     * 修改方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement update(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        if (!settlementRepository.findByMerchantAndReconcileDate(settlement.getMerchant().getId(), settlement.getReconcileBeginDate()).isEmpty()) {
            throw new RuntimeException("后续账期已对账,此账期不允许更改操作!");
        }
        settlement.computeAmt();
        settlement.setAccumulate();
        settlement.getAccumulateSum();

        //对账人为空的时候给第一次编辑的人，有则不赋值了
        if(Objects.isNull(settlement.getReconcileUid())){
            settlement.setReconcileUid(request.getPerson());
            settlement.setReconcileDate(new Date());
        }


        //重新获取分期剩余未扣收金额
        double remainingDeductibleBalance = settlement.getCostSummaries().stream().mapToDouble(SettlementCostSummary::getRemainingDeductibleBalance).sum();
        settlement.setTotalUnDeductedCost(Arith.round(remainingDeductibleBalance, 2));

        freezeIncomeItem(findAccountSettlement(settlement.getId()), -1); //还原费用账单冻结
        freezeIncomeItem(settlement, 1); //回写费用账单冻结

        // 重新获取平台费用，写入营业支出平台费用
        costSummsryUpdate(settlement);

        settlement.computeAmt();
        //重新给收款账户赋值，并且判断是否和总计应结算营业额一致
        if (settlement.getMerchantBankAccounts().size() == 1) {
            setMerchantBankAccount(settlement);
        }

        businessDebitNoteUpdate(settlement);//还原预支单

        businessDebitNoteSave(settlement);//重写预支单
        return settlementRepository.saveAndFlush(settlement);
    }

    /**
     * 修改回执
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement updateReceipt(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        return settlementRepository.saveAndFlush(settlement);
    }

    /**
     * 重新获取营业支出 平台费用
     *
     * @param settlement
     */
    private void costSummsryUpdate(MerchantAccountSettlement settlement) {
        //重新获取平台费用，写入营业支出平台费用
        settlement.getCostSummaries().forEach(costSummary -> {
            if (!"PLATFORM_FEE".equals(costSummary.getExpenditureSummary().getCode())) return;

            //本期新增  上期扣收 == 0 求和费用金额
            double amount = settlement.getExpenseItems().stream().filter(sum -> sum.getAmountDeducted() == 0)
                    .mapToDouble(SettlementExpenseItem::getAmount).sum();
            costSummary.setCurrentPeriodShouldDeducted(Arith.round(amount, 2));

            //上期未扣收 上期扣收 != 0  求和费用金额-求和上期扣收
            double amountSum = settlement.getExpenseItems().stream().filter(sum -> sum.getAmountDeducted() != 0)
                    .mapToDouble(SettlementExpenseItem::getAmount).sum();
            double amountDeducted = settlement.getExpenseItems().stream().filter(sum -> sum.getAmountDeducted() != 0)
                    .mapToDouble(SettlementExpenseItem::getAmountDeducted).sum();
            costSummary.setPreviousNotDeducted(Arith.round(amountSum - amountDeducted, 2));

            //剩余应扣余额 求和剩余应扣余额
            double balanceAmount = settlement.getExpenseItems().stream().mapToDouble(SettlementExpenseItem::getBalanceAmount).sum();
            costSummary.setRemainingDeductibleBalance(Arith.round(balanceAmount, 2));
        });
    }


    /**
     * 删除方法
     *
     * @param request
     * @return
     */
    public String delete(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        checkData(request);

        if (!settlementRepository.findByMerchantAndReconcileDate(settlement.getMerchant().getId(), settlement.getReconcileBeginDate()).isEmpty())
            throw new RuntimeException("后续账期已对账,此账期不允许更改操作!");

        if ("squareAccounts".equals(settlement.getStateAccounts()))
            throw new RuntimeException(String.format("已清账无法退回!", settlement.getId()));

        freezeIncomeItem(findAccountSettlement(settlement.getId()), - 1); //还原

        List<MerchantFixedLoss> merchantFixedLosses = merchantFixedLossRepository.findCompany(settlement.getMerchant().getId(),
                DateUtil.format(settlement.getReconcileBeginDate(), DateUtil.SHOW_DATE_FORMAT),
                DateUtil.format(settlement.getReconcileEndDate(), DateUtil.SHOW_DATE_FORMAT));
        merchantFixedLossRepository.deleteAll(merchantFixedLosses);

        businessDebitNoteDelete(settlement);//删除时还原预支单

        settlementRepository.delete(settlement);
        return "删除成功";
    }

    private MerchantAccountSettlement findAccountSettlement(String settlementId) {
        MerchantAccountSettlement accountSettlement = settlementRepository.findOne(settlementId);
        if (accountSettlement == null) {
            new RuntimeException("单据已删除，请刷新后重新操作！");
        }
        return accountSettlement;
    }

    /**
     * 财务审核方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement approval(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        settlement.setState("financialAudit");
        settlement.setStateAccounts("outstandingAccount");//是否清账
        settlement.setFinancialAuditUid(request.getPerson());
        settlement.setFinancialAuditDate(new Date());

        if(Objects.isNull(settlement.getReconcileUid())){
            settlement.setReconcileUid(request.getPerson());
            settlement.setReconcileDate(new Date());
        }

        //获取该商户下赋能店对账岗位
        List<Post> postList = SpringManager.getBean(PostRepository.class).findAll(JpaDsl.toCriteriaByEq("company.id",
                settlement.getMerchant().getId())).stream().filter(post -> "0000792".equals(post.getWorkPost().getId())).collect(Collectors.toList());
        List<User> userList = new ArrayList<>();
        for (Post post : postList) {
            List<User> users = SpringManager.getBean(UserRepository.class).findByPost(post.getId());
            userList.addAll(users);
        }

        //OA对账人
        User user = SpringManager.getBean(UserRepository.class).findAll(JpaDsl.toCriteriaByEq("origin", settlement.getReconcileUid().getId())).get(0);
        settlement.setReconcile(Objects.isNull(user) ? null : user.getId().toString());

        //OA商户确认人
        JSONArray array = userList.stream().distinct().map(User::getId).collect(Collectors.toCollection(JSONArray::new));
        settlement.setCandidates(JSON.toJSONString(array));
        settlement.setSettleBusinessMoneyTotal(Arith.round(settlement.getSettleBusinessMoneyTotal(), 2));
        if (settlement.getSettleBusinessMoneyTotal() <= 0 || settlement.getMerchantBankAccounts().size() == 1) {
            settlement.setTotalOa("0");
        } else {
            settlement.setTotalOa("1");
        }
        return settlementRepository.saveAndFlush(settlement);
    }

    /**
     * 添加商户银行账户信息
     *
     * @param settlement MerchantAccountSettlement
     */
    private void setMerchantBankAccount(MerchantAccountSettlement settlement) {
        List<MerchantBankAccount> merchantBankAccounts = new ArrayList<>();
        settlement.getMerchantBankAccounts().forEach(bankAccount -> {
            double amt = settlement.getSettleBusinessMoneyTotal() < 0 ? 0 : settlement.getSettleBusinessMoneyTotal();
            bankAccount.setAmount(Arith.round(amt, 2));
            merchantBankAccounts.add(bankAccount);
        });
        settlement.setMerchantBankAccounts(merchantBankAccounts);
    }

    /**
     * 商户确认方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement merchantConfirmation(SimpleRequest request) {
        checkData(request);
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);

        settlement.setMerchantConfirmationUid(request.getPerson());
        settlement.setMerchantConfirmationDate(new Date());
        Double sum = settlement.getMerchantBankAccounts().stream().mapToDouble(MerchantBankAccount::getAmount).sum();

        if ("1".equals(request.getJO().get("wf_variable_wf_state"))) {
            if (settlement.getSettleBusinessMoneyTotal() > 0 && sum == 0)
                throw new RuntimeException("请将总计应结算营业款,分配到收款账户的收款金额!");
            if (Arith.round(settlement.getSettleBusinessMoneyTotal(), 2) > 0 && Arith.round(settlement.getSettleBusinessMoneyTotal(), 2) != Arith.round(sum, 2))
                throw new RuntimeException("总计应结算营业款不等于收款账户的收款金额，请确认");

            //检查上期单据是否已确认
            settlementRepository.findOne(JpaDsl.toCriteriaByEq("merchant.id", settlement.getMerchant().getId(),
                    "reconcileEndDate", DateUtil.add(settlement.getReconcileBeginDate(), Calendar.DAY_OF_MONTH, -1))).ifPresent(m -> {
                if ("sent".equals(m.getState()) || "financialAudit".equals(m.getState())) {
                    throw new RuntimeException(String.format("上期对账单[%s]未确认，请先确认上期对账单！", m.getId()));
                }
            });
        }
        //获取该商户下赋能店对账岗位
        List<Post> postList = SpringManager.getBean(PostRepository.class).findAll(JpaDsl.toCriteriaByEq("company.id",
                settlement.getMerchant().getId())).stream().filter(post -> "0000792".equals(post.getWorkPost().getId())).collect(Collectors.toList());
        List<User> userList = new ArrayList<>();
        for (Post post : postList) {
            List<User> users = SpringManager.getBean(UserRepository.class).findByPost(post.getId());
            userList.addAll(users);
        }

        //OA对账人
        User user = SpringManager.getBean(UserRepository.class).findAll(JpaDsl.toCriteriaByEq("origin", settlement.getReconcileUid().getId())).get(0);
        settlement.setReconcile(Objects.isNull(user) ? null : user.getId().toString());

        //OA商户确认人
        JSONArray array = userList.stream().distinct().map(User::getId).collect(Collectors.toCollection(JSONArray::new));
        settlement.setCandidates(JSON.toJSONString(array));

        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlement);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * 商户确认方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement merchantAddress(SimpleRequest request) {
        checkData(request);
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        //检查上期单据是否已确认
        settlementRepository.findOne(JpaDsl.toCriteriaByEq("merchant.id", settlement.getMerchant().getId(),
                "reconcileEndDate", DateUtil.add(settlement.getReconcileBeginDate(), Calendar.DAY_OF_MONTH, -1))).ifPresent(m -> {
            if ("sent".equals(m.getState()) || "financialAudit".equals(m.getState())) {
                throw new RuntimeException(String.format("上期对账单[%s]未确认，请先确认上期对账单！", m.getId()));
            }
        });
        if ("1".equals(request.getJO().get("wf_variable_wf_state"))) {
            settlement.setState("confirmation");
            if (Objects.isNull(settlement.getMerchantAddress()) || settlement.getMerchantAddress().length() < 3)
                throw new RuntimeException("请上传商户确认回执!");
        } else {
            settlement.setState("financialAudit");
        }

        settlement.setMerchantConfirmationUid(request.getPerson());
        settlement.setMerchantConfirmationDate(new Date());
        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlement);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * 商户驳回方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement merchantReject(SimpleRequest request) {
        checkData(request);
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        MerchantAccountSettlement settlementRepositoryOne = settlementRepository.getOne(settlement.getId());

        //清账无法退回
        if ("squareAccounts".equals(settlementRepositoryOne.getStateAccounts()))
            throw new RuntimeException(String.format("此单平台已清账，不允许拒绝。如有账目问题请与对账组人员联系！", settlement.getId()));
        settlementRepositoryOne.setState("sent");
        settlementRepositoryOne.setStateAccounts(null);//是否清账

        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlementRepositoryOne);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * 数据检查
     *
     * @param request
     */
    public void checkData(SimpleRequest request) {
        String id = request.get("id");
        MerchantAccountSettlement settlement = settlementRepository.findOne(id);
        Integer version = request.get("version");
        Optional.ofNullable(settlement).orElseThrow(() -> new RuntimeException(String.format("赋能商户结算对账单[%s]在系统中不存在,请检查数据", id)));
        if (settlement.getVersion() != version) {
            throw new RuntimeException("记录已更新或已删除，请重新提交！");
        }

    }

    /**
     * 清账
     *
     * @param request
     */
    public MerchantAccountSettlement squareAccounts(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        checkData(request);

        //processingDeductionDetail(settlement);
        settlement.setStateAccounts("squareAccounts");
        settlement.setSquareAccountsDate(new Date());
        settlement.setSquareAccountsUid(request.getPerson());


        extracted(settlement);//回写总账
        return settlementRepository.saveAndFlush(settlement);
    }


    /**
     * 回写总账
     *
     * @param settlement
     */
    private void extracted(MerchantAccountSettlement settlement) {
        settlement.getExpenseItems().stream().collect(Collectors.groupingBy(SettlementExpenseItem::getPartnerName)).forEach((partnerId, ExpenseItemList) -> {
            Income income = incomeRepository.findOne(ExpenseItemList.get(0).getExpenseId());
            PartyRole partner = partyRoleRepository.findOne(partnerId);
            Map<String, Object> paramMap = MapUtil.mapper("company", income.getCompany(), "partner", partner);
            if ("Supplier".equals(partner.getRole().getId())) {
                double amount = ExpenseItemList.stream().mapToDouble(SettlementExpenseItem::getCurrentAmount).sum() * -1;
                // 返利：未清项 减少，返利：冻结项 增加
                transaction(income, settlement, "discount_unsettled", -1 * amount, "discount_freeze", amount);
                // 返利：冻结项 减少，返利：已清项 增加
                transaction(income, settlement, "discount_freeze", -1 * amount, "discount_settled", amount);
            } else {
                double amount = ExpenseItemList.stream().mapToDouble(SettlementExpenseItem::getCurrentAmount).sum();
                // 返利：未清项 减少，返利：冻结项 增加
                transaction(income, settlement, "discount_unsettled", -1 * amount, "discount_freeze", amount);
                // 返利：冻结项 减少，返利：已清项 增加
                transaction(income, settlement, "discount_freeze", -1 * amount, "discount_settled", amount);
            }
        });
    }

    /**
     * 写入总账
     */
    private void transaction(Income income, MerchantAccountSettlement settlement, Object... items) {
        ledgerService.transaction(income.getCompany(), income.getDept(), income.getPartner(),
                income.getCooperation().getInventoryMode(), income.getCooperation().getAccountingMethod(),
                settlement.getId(), "MerchantAccountSettlement", new Date(), items);
    }

    /**
     * 处理费用单与借支单
     *
     * @param settlement
     */
    public void processingExpenseBill(MerchantAccountSettlement settlement, double changeQty) {
        settlement.getExpenseItems().forEach(merchantExpenseItem -> {
            IncomeItem expenseItem = merchantExpenseItem.getIncomeItem();
            if (expenseItem != null) {
                expenseItem.setFreezeAmount(expenseItem.getFreezeAmount() + (merchantExpenseItem.getCurrentAmount() * changeQty));
                incomeItemRepository.saveAndFlush(expenseItem);
            } else {
                Income one = incomeRepository.findOne(merchantExpenseItem.getId());
                if (one != null) {
                    one.getIncomeItems().forEach(e -> {
                        IncomeItem expenseItem1 = e;
                        expenseItem1.setFreezeAmount(e.getFreezeAmount() + (merchantExpenseItem.getCurrentAmount() * changeQty));
                        IncomeItem expenseItem2 = incomeItemRepository.saveAndFlush(expenseItem1);
                        merchantExpenseItem.setIncomeItem(expenseItem2);
                    });
                }

            }
        });

        settlement.getBusinessDebitItems().forEach(merchantBusinessDebitItem -> {
            BusinessDebitNote businessDebitNote = merchantBusinessDebitItem.getBusinessDebitNote();
            businessDebitNote.setPrepaidPrincipal(businessDebitNote.getPrepaidPrincipal() + (merchantBusinessDebitItem.getCurrentAmount() * changeQty));
            businessDebitNoteRepository.saveAndFlush(businessDebitNote);
        });
    }

    /**
     * 处理扣收明细
     *
     * @param settlement
     */
    public void processingDeductionDetail(MerchantAccountSettlement settlement) {
        settlement.getBusinessDebitItems().forEach(merchantBusinessDebitItem -> {
            BusinessDebitNote businessDebitNote = merchantBusinessDebitItem.getBusinessDebitNote();
            DeductionDetail deductionDetail = new DeductionDetail();
            deductionDetail.setId(SpringManager.getBean(SequenceService.class).getId(BusinessDebitNote.CONST_TABLE_NAME));
            deductionDetail.setSettlement(settlement);
            deductionDetail.setBusinessDebitNote(businessDebitNote);
            deductionDetail.setDeductionDate(new Date());
            deductionDetail.setPreviousPrincipal(merchantBusinessDebitItem.getPriceDebit() - merchantBusinessDebitItem.getPrincipalRepaid());
            deductionDetail.setRepaymentAmount(merchantBusinessDebitItem.getCurrentAmount());
            deductionDetail.setCurrentPrincipal(deductionDetail.getPreviousPrincipal() - deductionDetail.getRepaymentAmount());

            businessDebitNote.setPrincipalRepaid(businessDebitNote.getPrincipalRepaid() + merchantBusinessDebitItem.getCurrentAmount());
            businessDebitNote.setPrepaidPrincipal(businessDebitNote.getPrepaidPrincipal() - merchantBusinessDebitItem.getCurrentAmount());
            businessDebitNote.setUnpaidPrincipal(businessDebitNote.getPriceDebit() - businessDebitNote.getPrincipalRepaid());
            businessDebitNote.setCapitalBalance(businessDebitNote.getPriceDebit() - businessDebitNote.getPrincipalRepaid());
            businessDebitNote.getDeductionDetails().add(deductionDetail);
            businessDebitNote.setUnDeductedLoanAmount(businessDebitNote.getUnDeductedLoanAmount() - merchantBusinessDebitItem.getCurrentAmount());
            businessDebitNote.setState(businessDebitNote.getUnpaidPrincipal() == 0 ? "paidOff" : "partialDeduction");
            businessDebitNoteRepository.saveAndFlush(businessDebitNote);

        });
    }

    /**
     * 获取该商户目前所有待扣收费用单
     *
     * @param merchantId
     * @return
     */
    public void getExpenseItems(String merchantId, MerchantAccountSettlement settlement) {
        //获取截止本期为止的待扣收费用单
        List<IncomeItem> expenseItems = incomeItemRepository.findAllByCompanys(merchantId, settlement.getReconcileEndDate());
        List<SettlementExpenseItem> merchantExpenseItems = new ArrayList<>(expenseItems.size());
        expenseItems.forEach(expenseItem -> {
            SettlementExpenseItem merchantExpenseItem = new SettlementExpenseItem();
            merchantExpenseItem.setIncomeItem(expenseItem);
            PartyRole one = partyRoleRepository.findOne(expenseItem.getIncome().getPartner().getId());
            if (one != null && "Supplier".equals(one.getRole().getId())) {
                merchantExpenseItem.setAmount(expenseItem.getIncomeAmount() * -1);
            } else {
                merchantExpenseItem.setAmount(expenseItem.getIncomeAmount());
            }
            merchantExpenseItem.setAmountDeducted(Arith.add(expenseItem.getRecoveryAmount(), expenseItem.getFreezeAmount()));
            merchantExpenseItem.setCurrentAmount(merchantExpenseItem.getAmount() - merchantExpenseItem.getAmountDeducted());
            merchantExpenseItems.add(merchantExpenseItem);
        });

        settlement.setExpenseItems(merchantExpenseItems);
        //刷新对账成本中的平台费用成本
        SettlementCostSummary costSummary = settlement.getCostSummaries().stream().filter(cost ->
                "PLATFORM_FEE".equals(cost.getExpenditureSummary().getId())).findFirst().orElseThrow(() -> new RuntimeException("对账单中无费用成本项！数据有误！"));
        costSummary.setCurrentPeriodShouldDeducted(merchantExpenseItems.stream().filter(sum ->
                sum.getCurrentAmount() == sum.getAmount()).map(SettlementExpenseItem::getAmount).reduce(Arith::add).orElse(0.0));
        costSummary.setPreviousNotDeducted(merchantExpenseItems.stream().filter(sum -> sum.getCurrentAmount() != sum.getAmount()).map(SettlementExpenseItem::getAmount)
                .reduce(Arith::add).orElse(0.0) - merchantExpenseItems.stream().filter(sum -> sum.getCurrentAmount() != sum.getAmount())
                .map(SettlementExpenseItem::getAmountDeducted).reduce(Arith::add).orElse(0.0));
        costSummary.setCurrentPeriodActualDeducted(costSummary.getCurrentPeriodShouldDeducted() + costSummary.getPreviousNotDeducted());
        costSummary.setRemainingDeductibleBalance(costSummary.getCurrentPeriodShouldDeducted() -
                costSummary.getCurrentPeriodActualDeducted() + costSummary.getPreviousNotDeducted());
    }

    /**
     * 获取该商户目前所有待扣借支单单
     *
     * @param merchantId
     * @return
     */
    public void getBusinessDebitItem(String merchantId, MerchantAccountSettlement settlement) {
        //获取截止本期为止的待扣收费用单
        List<BusinessDebitNote> businessDebitNote = businessDebitNoteRepository.findAllByFnCustomerId(merchantId, getAddDay(settlement.getReconcileEndDate()));
        List<SettlementBusinessDebitItem> businessDebitItems = new ArrayList<>(businessDebitNote.size());
        businessDebitNote.forEach(debitNote -> {
            SettlementBusinessDebitItem businessDebitItem = new SettlementBusinessDebitItem();
            businessDebitItem.setBusinessDebitNote(debitNote);
            businessDebitItem.setPriceDebit(debitNote.getCapitalBalance());//预支金额
            businessDebitItem.setPrincipalRepaid(debitNote.getUnBuckledBudgetAmount());//上期已扣收
            businessDebitItem.setCurrentAmount(businessDebitItem.getPriceDebit() - businessDebitItem.getPrincipalRepaid());//本期扣收金额 = 预支金额-上期已扣收
            businessDebitItem.setBalanceAmount(businessDebitItem.getPriceDebit() - businessDebitItem.getCurrentAmount() - businessDebitItem.getPrincipalRepaid());//结余
            businessDebitItem.setReason(debitNote.getReason());//预支说明
            businessDebitItem.setLoanDate(debitNote.getLoanDate());//付款日期
            businessDebitItem.setLimitDate(debitNote.getLimitDate());//预支期起
            businessDebitItem.setBorrowingDate(debitNote.getBorrowingDate());//预支期止
            businessDebitItems.add(businessDebitItem);
        });
        settlement.setBusinessDebitItems(businessDebitItems);

        //刷新对账成本中的预支成本
        SettlementCostSummary costSummary = settlement.getCostSummaries().stream().filter(cost ->
                "BORROW_MONEY".equals(cost.getExpenditureSummary().getId())).findFirst().orElseThrow(() -> new RuntimeException("对账单中无费用成本项！数据有误！"));
        costSummary.setCurrentPeriodShouldDeducted(businessDebitItems.stream().filter(sum ->
                sum.getPrincipalRepaid() == 0).map(SettlementBusinessDebitItem::getCurrentAmount).reduce(Arith::add).orElse(0.0));
        costSummary.setPreviousNotDeducted(businessDebitItems.stream().filter(sum ->
                sum.getPrincipalRepaid() != 0).map(SettlementBusinessDebitItem::getCurrentAmount).reduce(Arith::add).orElse(0.0));
        costSummary.setCurrentPeriodActualDeducted(costSummary.getCurrentPeriodShouldDeducted() + costSummary.getPreviousNotDeducted());
        costSummary.setRemainingDeductibleBalance(costSummary.getCurrentPeriodShouldDeducted() -
                costSummary.getCurrentPeriodActualDeducted() + costSummary.getPreviousNotDeducted());
    }

    /**
     * 校验政策
     *
     * @param merchantId
     * @param endDate
     */
    public void checkPolicy(String merchantId, Date startDate, Date endDate) {
        List<PartnerCheck> partnerChecks = partnerCheckRepository.findAllByFnCustomerAndState(merchantId, startDate, endDate, "approve");
        if (partnerChecks.isEmpty()) {
            throw new RuntimeException(String.format("当前商户[%s] 不存在有效的赋能商户政策！请联系总部定义", merchantId));
        }
        if (partnerChecks.size() != 1) {
            throw new RuntimeException(String.format("当前商户[%s] 存在多张有效的赋能商户政策！请联系总部排查", merchantId));
        }
    }

    /**
     * 获取上期商户对账单
     *
     * @param merchantId
     * @param reconcileBeginDate
     * @return
     */
    public MerchantAccountSettlement getThePreviousMerchantSettlement(Date reconcileBeginDate, String merchantId) {
        //业务场景为 上一次对账结束时间+1天即为此次对账开始时间
        //获取上期对账单
        List<MerchantAccountSettlement> settlement = settlementRepository.findByMaxReconcileEndDate(merchantId);
        if (settlement.size() == 0) return null;
        if (settlement.size() > 1) throw new RuntimeException(String.format("获取上期对账单异常！存在多张上期对账单！请检查数据!"));
        Date previousReconcileEndDate = DateUtil.add(reconcileBeginDate, Calendar.DAY_OF_MONTH, -1);
        if (settlement.size() <= 1 && previousReconcileEndDate.compareTo(settlement.get(0).getReconcileEndDate()) != 0) {
            throw new RuntimeException(String.format("商户[%s]此次对账开始日期有误！应为上次对账日期[%s]+1天", merchantId, DateUtil.format(settlement.get(0).getReconcileEndDate(), "yyyy-MM-dd")));
        }
        return settlement.get(0);
    }

    /**
     * 获取日期+1
     *
     * @param date
     * @return
     */
    public static Date getAddDay(Date date) {
        return DateUtil.add(date, Calendar.DAY_OF_MONTH, 1);
    }

    public Map error(SimpleRequest request) {

        return MapUtil.mapper("content", new JSONArray(), "total", 0, "size", 0, "msg", "此项目无明细数据！");
    }

    public MerchantAccountSettlement empower(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        if (!"1".equals(request.getJO().get("wf_variable_wf_state"))) return settlement;
        if (settlement.getSettleBusinessMoneyTotal() <= 0) {
            settlement.setState("payment");
        } else {
            settlement.setState("pending");
        }
        backIncomeItem(settlement); //回写费用账单
        settlement.setServiceApproveDate(new Date());
        settlement.setServiceApproveUid(request.getPerson());
        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlement);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * 付款单回写2.0付款金额
     *
     * @param request
     */
    public void payment(SimpleRequest request) {
        Map<String, Object> map = new HashMap<>(request.get("data"));
        Payment payment = (Payment) map.get("payment");
        List<MerchantBankAccount> merchantBankAccounts = new ArrayList<>();
        payment.getPaymentItemSet().stream().forEach(v -> {
            if (Objects.isNull(v.getSource())) return;

            //回写2.0收款账户
            List<MerchantBankAccount> bankAccountId = merchantBankAccountRepository.findAll(JpaDsl.toCriteriaByEq("merchantSettlement.id", v.getSource(), "accountId.accountId", v.getBankAccount()));
            if (bankAccountId.size() <= 0) return;
            MerchantBankAccount merchantBankAccount = bankAccountId.get(0);
            merchantBankAccount.setPayAmount(merchantBankAccount.getPayAmount() + v.getAmount());
            settlement(v, payment);//付款单回写2.0 实体
            merchantBankAccounts.add(merchantBankAccount);
        });

        merchantBankAccountRepository.saveAll(merchantBankAccounts);
    }

    /**
     * 付款单回写2.0 实体
     *
     * @param paymentItem,payment
     */
    private void settlement(PaymentItem paymentItem, Payment payment) {
        if (Objects.isNull(payment.getFileAddres())) throw new RuntimeException("需要上传付款回执,请上传！！");
        MerchantAccountSettlement settlement = settlementRepository.findOne(paymentItem.getSource());
        //回写付款回执，多个对账单出一期付款单 则回执全回写，  4.14 花姐确定

        JSONArray fileAddress = JSONArray.parseArray(payment.getFileAddres());

        JSONArray settlementPNG = new JSONArray();
        if (Objects.nonNull(settlement.getFileAddres())) {
            settlementPNG = JSONArray.parseArray(settlement.getFileAddres());
            fileAddress.addAll(settlementPNG);
        }
        settlement.setFileAddres(fileAddress.toJSONString());

        settlement.setPayAmount(settlement.getPayAmount() + paymentItem.getAmount());
        if (Arith.compare(settlement.getPayAmount(), settlement.getSettleBusinessMoneyTotal(), 2) == 0) {
            settlement.setState("payment");
        } else {
            settlement.setState("pay");
        }

        settlementRepository.saveAndFlush(settlement);
    }

    private MerchantAccountSettlement OAFlag(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        settlement.setPic(true);
        if (settlement.getSettleBusinessMoneyTotal() <= 0 || settlement.getMerchantBankAccounts().size() == 1) {
            settlement.setTotalOa("0");
        } else {
            settlement.setTotalOa("1");
        }
        return settlement;
    }

    /**
     * 商户驳回方法
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement settlementReject(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        if (!"1".equals(request.getJO().get("wf_variable_wf_state"))) {
            settlement.setState("financialAudit");
        }
        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlement);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * OA流程中修改余额校对
     *
     * @param request
     * @return
     */
    public MerchantAccountSettlement OAUpdate(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        if (!"1".equals(request.getJO().get("wf_variable_wf_state"))) return settlement;
        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlement);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * 财务副总裁审批
     * @param request
     * @return
     */
    public MerchantAccountSettlement financeSupremo(SimpleRequest request) {
        MerchantAccountSettlement settlement = request.getO(MerchantAccountSettlement.class);
        if (!"1".equals(request.getJO().get("wf_variable_wf_state"))) return settlement;
        if (settlement.getSettleBusinessMoneyTotal() <= 0) {
            settlement.setState("payment");
        } else {
            settlement.setState("pending");
        }
        backIncomeItem(settlement); //回写费用账单
        settlement.setServiceApproveDate(new Date());
        settlement.setServiceApproveUid(request.getPerson());

        //回写已扣收金额
        settlement.getExpenseItems().forEach(v -> {
            if (Objects.nonNull(v.getIncomeItem()) && !StringUtils.isEmpty(v.getIncomeItem().getRemark())) {
                ExpenseItem expenseItem = expenseItemRepository.findOne(v.getIncomeItem().getRemark());
                if (Objects.nonNull(expenseItem)) {
                    expenseItem.setAmountDeducted(expenseItem.getAmountDeducted() + v.getCurrentAmount());
                    expenseItemRepository.saveAndFlush(expenseItem);
                }
            }
        });

        MerchantAccountSettlement merchantAccountSettlement = settlementRepository.saveAndFlush(settlement);
        merchantAccountSettlement.setWrite(false);
        return merchantAccountSettlement;
    }

    /**
     * 检查时间是否在结算期起止内
     * @param request
     * @return
     */
    public Boolean checkSettlementDate(SimpleRequest request){
        JSONObject requestJO = request.getJO();
        String id = requestJO.getString("id");
        Date reconcileEndDate = DateUtil.dateToDate(requestJO.getDate("reconcileDate"), DateUtil.SHOW_DATE_FORMAT);
        //根据商户id获取历史所有对账单,根据传入日期判断历史账单是否含有重复日期的
        List<MerchantAccountSettlement> settlements = settlementRepository.findAll(JpaDsl.toCriteriaIgnoreNull("merchant.id", "EQ", id));
        AtomicBoolean flag = new AtomicBoolean(false);
        if (!CollectionUtils.isEmpty(settlements)){
            try {
                settlements.forEach(item -> {
                    boolean belongCalendar = DateUtil.belongCalendar(reconcileEndDate,
                            DateUtil.dateToDate(item.getReconcileBeginDate(), DateUtil.SHOW_DATE_FORMAT),
                            DateUtil.dateToDate(item.getReconcileEndDate(), DateUtil.SHOW_DATE_FORMAT));
                    if (belongCalendar){
                        flag.set(true);
                        throw new RuntimeException("终止forEach循环,减少无意义比较");
                    }
                });
            }catch (Exception e){}
        }
        return flag.get();
    }

    private Map findOrderInfo(SimpleRequest request) {
        JSONObject json = request.getJO();
        JSONObject param = parseCondition(json.getJSONArray("condition"));

        JSONArray all = accountSettlementDao.findDept(param, request.getPostId(), request.get("page"), request.get("size"));
        Object count = accountSettlementDao.findCount(param, request.getPostId());
        JSONObject result = new JSONObject();
        result.put("content", all);
        result.put("total", ((JSONObject) ((JSONArray) count).get(0)).get("COUNT"));
        result.put("pages", ((JSONObject) ((JSONArray) count).get(0)).get("COUNT"));
        return result;
    }


    /**
     * 解析前端参数
     *
     * @param array filter 参数，解析日期格式
     * @return
     */
    public JSONObject parseCondition(JSONArray array) {
        JSONObject model = new JSONObject();
        array.forEach(v -> {
            JSONObject filter = (JSONObject) v;
            if ("GTE".equals(filter.getString("operator")) && "reconcile_date".equals(filter.getString("field"))) {
                model.put(filter.getString("field") + "_DATE", filter.getString("value"));//因时间类型的查询条件有两个值，不好区分,所以在字段名后加_DATE
            } else if ("GTE".equals(filter.getString("operator")) && "reconcile_begin_date".equals(filter.getString("field"))) {
                model.put(filter.getString("field") + "_DATE", filter.getString("value"));//因时间类型的查询条件有两个值，不好区分,所以在字段名后加_DATE
            } else if ("GTE".equals(filter.getString("operator")) && "reconcile_end_date".equals(filter.getString("field"))) {
                model.put(filter.getString("field") + "_DATE", filter.getString("value"));//因时间类型的查询条件有两个值，不好区分,所以在字段名后加_DATE
            } else {
                model.put(filter.getString("field"), filter.get("value"));
            }
        });
        return model;
    }

}
